package com.shijie.sjoj.router.filter;

import com.shijie.sjoj.model.base.ResponseBean;
import com.shijie.sjoj.model.component.CryptoModel;
import com.shijie.sjoj.model.utils.JsonFileUtils;
import com.shijie.sjoj.model.utils.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;


/**
 * 请求体转换（数据解密）过滤器
 *
 * @author ZSJ
 */
//@Component
@Order(1)
public class RequestBodyConvertFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        // 获取请求实例
        ServerHttpRequest request = exchange.getRequest();
        // 获取响应实例
        ServerHttpResponse response = exchange.getResponse();
        // 校验请求体是否开启转换（加密）
        if (this.verifyCrypto(request.getHeaders())) {
            // 返回链式调用
            return DataBufferUtils.join(request.getBody())
                    .flatMap(dataBuffer -> {
                        // 获取缓冲区可读的字节大小
                        int count = dataBuffer.readableByteCount();
                        // 初始化数据字节数组
                        byte[] enSource = new byte[count];
                        // 读取数据
                        dataBuffer.read(enSource);
                        // 释放缓冲区
                        DataBufferUtils.release(dataBuffer);
                        // 请求体密文
                        String cipher = new String(enSource);
                        // 是否转换校验
                        if (StringUtils.isBlank(cipher)) {
                            return chain.filter(exchange);
                        }
                        try {
                            // 请求体转换（解密）
                            String body = CryptoModel.decryptData(cipher);
                            // 构建新请求
                            ServerHttpRequest newRequest = new ServerHttpRequestDecorator(request) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    // 获取请求体源数据字节数组
                                    byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
                                    // 返回请求体
                                    return Flux.just(new DefaultDataBufferFactory().wrap(bytes));
                                }
                            };
                            // 返回新请求的链式调用
                            return chain.filter(exchange.mutate().request(newRequest).build());
                        } catch (Exception e) {
                            // 转换发生错误
                            String err = JsonFileUtils.toJson(ResponseBean.error("请求体转换错误"));
                            byte[] errBytes = err.getBytes(StandardCharsets.UTF_8);
                            // 返回响应
                            return response.writeWith(Mono.just(response.bufferFactory().wrap(errBytes)));
                        }
                    });
        }
        // 继续向下执行
        return chain.filter(exchange);
    }

    private boolean verifyCrypto(HttpHeaders headers) {
        String[] opens = {"true", "on", "up"};
        String crypto = headers.getFirst("crypto");
        String sjojCrypto = headers.getFirst("sjoj-crypto");
        for (String open : opens) {
            if (open.equals(crypto) || open.equals(sjojCrypto)) {
                return true;
            }
        }
        return false;
    }

}
